import * as path from "path";
import * as fs from "fs";
import IRequest from "./interfaces/IRequest";
import IResponse from "./interfaces/IResponse";

export function create(applicationsRoot: string): any {
    let actions = new Map<string, any>();
    do {
        if (!fs.existsSync(applicationsRoot)) {
            console.warn(`${applicationsRoot} not exists`);
            break;
        }
        if (!fs.statSync(applicationsRoot).isDirectory()) {
            console.warn(`${applicationsRoot} is not a directory`);
            break;
        }
        for (let applicationName of fs.readdirSync(applicationsRoot)) {
            let applicationPath = path.join(applicationsRoot, applicationName);
            if (!fs.statSync(applicationPath).isDirectory()) {
                console.warn(`${applicationPath} is not a directory, skip it`);
                continue;
            }

            for (let controllerName of fs.readdirSync(applicationPath)) {
                let controllerPath = path.join(applicationPath, controllerName);
                if (!fs.statSync(controllerPath).isDirectory()) {
                    console.warn(`${controllerPath} is not a directory, skip it`);
                    continue;
                }

                for (let actionFileName of fs.readdirSync(controllerPath)) {
                    let actionPath = path.join(controllerPath, actionFileName);

                    if (!actionFileName.toLowerCase().endsWith(".js")) {
                        console.warn(`${actionPath} is not end with .js`);
                        continue;
                    }

                    if (!fs.statSync(actionPath).isFile()) {
                        console.warn(`${actionPath} is not a file`);
                        continue;
                    }

                    let {default: ActionClass} = require(actionPath);
                    let actionName = actionFileName.substring(0, actionFileName.length - 3);
                    actions.set(`/${applicationName}/${controllerName}/${actionName}`.toLowerCase(), ActionClass);
                    if (actionName.toLowerCase() == "index") {
                        actions.set(`/${applicationName}/${controllerName}`.toLowerCase(), ActionClass);
                        if (controllerName.toLowerCase() == "index") {
                            actions.set(`/${applicationName}`.toLowerCase(), ActionClass);
                            if (applicationName.toLowerCase() == "index") {
                                actions.set('/', ActionClass);
                            }
                        }
                    }
                }
            }
        }
    } while (false);
    console.log(__filename, "Scanned actions:", actions);

    return function (req: IRequest, res: IResponse, next: any) {
        let pathTokens: Array<string> = req.path.split("/");

        let applicationName = pathTokens[1];
        let controllerName = pathTokens[2];
        let actionName = pathTokens[3];
        if (pathTokens.length > 4) {
            pathTokens.splice(0, 4);
        } else {
            pathTokens.splice(0, pathTokens.length);
        }

        req.args = pathTokens;

        let key = "/";
        if (applicationName) {
            key += applicationName;
            if (controllerName) {
                key += `/${controllerName}`;
                if (actionName) {
                    key += `/${actionName}`;
                }
            }
        }

        key = key.toLowerCase();
        let ActionClass = actions.get(key);
        if (!ActionClass) {
            next();
            return;
        }

        let action = new ActionClass();
        let method = req.method.toLowerCase();
        if (!action[method]) {
            res.status(405).end("Method Not Allowed");
            return;
        }

        let actionResult = action[method](req, res);
        if (!(actionResult instanceof Promise)) {
            console.warn(__filename, `[${key}] Action result is not instance of Promise, this connection can not be closed gracefully, please take care of it by yourself`);
            return;
        }

        actionResult.then(value => {
            res.end();
        }).catch(reason => {
            console.warn(__filename, reason);
            res.status(500).end("Server internal error");
        });
    }
}